#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <errno.h>
#define PORT 12345
#define ClientNum 10
int main(int argc, char const *argv[])
{
    int socket_fd, client_fd;
    int client_count = 0;
    socklen_t client_lenth = 0;
    int socket_ret;
    int read_ret;
    int epoll_ret;
    int optval = 1;
    char readbuff[1024];
    struct sockaddr_in server_info;
    struct sockaddr_in client_info;
    /* 1 创建套接字 */
    socket_fd = socket(AF_INET, SOCK_STREAM, 0); // IPV4 TCP通信
    if (socket_fd == -1)
    {
        /* code */
        perror("socket\n");
        close(socket_fd);
        exit(-1);
    }
    /* 2 配置 允许端口复用  必须在bind绑定端口和地址之前使用*/
    socket_ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (int *)&optval, sizeof(int));
    if (socket_ret == -1)
    {
        /* code */
        perror("setsockopt\n");
        exit(EXIT_FAILURE);
    }
    /*3 绑定 端口号和地址 */
    memset(&server_info, 0, sizeof(struct sockaddr_in)); //清空结构体
    server_info.sin_family = AF_INET;
    server_info.sin_addr.s_addr = INADDR_ANY; //本地IP地址
    server_info.sin_port = htons(PORT);       //网络字节序转换
    socket_ret = bind(socket_fd, (struct sockaddr *)&server_info, sizeof(struct sockaddr_in));
    if (socket_ret == -1)
    {
        /* code */
        perror("bind\n");
        close(socket_fd);
        exit(-1);
    }
    /* 4 监听 客户端连接 */
    socket_ret = listen(socket_fd, ClientNum);
    if (socket_ret == -1)
    {
        /* code */
        perror("listen\n");
        close(socket_fd);
        exit(-1);
    }
    /* 初始化 epoll  */
    /* 创建一个 epoll文件描述符句柄 */
    int epoll_fd = epoll_create(ClientNum); //linux内核 2.6.8版本后 传入的参数已经失效
    struct epoll_event Epoll_Event;
    struct epoll_event *Events = (struct epoll_event *)malloc(sizeof(struct epoll_event) * ClientNum); //为返回事件的结构体数组分配内存

    /* 注册文件描述符 */
    Epoll_Event.data.fd = socket_fd;
    Epoll_Event.events = EPOLLIN | EPOLLET;                      //设置输入模式和边缘触发模式
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &Epoll_Event); //把socket——fd文件描述符向 epoll——fd文件描述符句柄中注册。
    while (1)
    {
        /* code */
        epoll_ret = epoll_wait(epoll_fd, Events, ClientNum, 1000000);
        if (epoll_ret == -1)
        {
            if (errno != EINTR)
            {
                /* code */
                perror("epoll_wait\n"); //出错
                continue;               //结束循环
            }
        }
        for (size_t i = 0; i < epoll_ret; i++)
        {
            /* code */
            if (Events[i].data.fd == socket_fd) //有新的客户端连接产生
            {
                memset(&client_info, 0, sizeof(struct sockaddr_in));                           //结构体数据清零
                client_fd = accept(socket_fd, (struct sockaddr *)&client_info, &client_lenth); //建立客户端连接 ，产生客户端连接的描述符
                if (client_fd == -1)
                {
                    perror("accept\n");
                    close(client_fd); //关闭客户端
                    continue;
                }
                if (client_count > ClientNum) //最大连接数超了
                {
                    printf("the client num than max connect num\n");
                    close(client_fd); //关闭客户端
                    continue;
                }
                client_count++;
                /* 注册新的文件描述符 */
                Epoll_Event.data.fd = client_fd;
                Epoll_Event.events = EPOLLIN | EPOLLET;                      //设置输入模式和边缘触发模式
                epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &Epoll_Event); //把socket——fd文件描述符向 epoll——fd文件描述符句柄中注册。
            }
            else if (Events[i].events & EPOLLIN) //有输出进来了
            {
                memset(readbuff, 0, sizeof(readbuff)); //清空缓冲区
                read_ret = recv(Events[i].data.fd, readbuff, 1024, 0);
                switch (read_ret)
                {
                case -1: //出错，或者超时
                    printf("reciver data error\n");
                    /* code */
                    break;
                case 0: /* 客户端断开连接 */
                    printf("%d client close\n", Events[i].data.fd);
                    close(Events[i].data.fd);
                    break;
                default:
                    printf("recive data form client %d\n", Events[i].data.fd);
                    printf("%s\n", readbuff);
                    break;
                }
            }
        }
    }
    free(Events); //释放内存
    return 0;
}
